﻿<script type="text/javascript">
	document.body.addEventListener( 'click', function(){
		alert(2);
	}, false );

	document.body.click(); // 模拟用户点击

	document.body.addEventListener( 'click', function(){
		alert(2);
	}, false );
	document.body.addEventListener( 'click', function(){
		112 第8 章 发布订阅模式
		alert(3);
	}, false );
	document.body.addEventListener( 'click', function(){
		alert(4);
	}, false );

	document.body.click(); // 模拟用户点击

	var salesOffices = {}; // 定义售楼处

	salesOffices.clientList = []; // 缓存列表，存放订阅者的回调函数
	salesOffices.listen = function( fn ){ // 增加订阅者
		this.clientList.push( fn ); // 订阅的消息添加进缓存列表
	};
	salesOffices.trigger = function(){ // 发布消息
		for( var i = 0, fn; fn = this.clientList[ i++ ]; ){
			fn.apply( this, arguments ); // (2) // arguments 是发布消息时带上的参数
		}
	};
	//下面我们来进行一些简单的测试：
	salesOffices.listen( function( price, squareMeter ){ // 小明订阅消息
		console.log( '价格= ' + price );
		console.log( 'squareMeter= ' + squareMeter );
	});

	salesOffices.listen( function( price, squareMeter ){ // 小红订阅消息
		console.log( '价格= ' + price );

		console.log( 'squareMeter= ' + squareMeter );
	});

	salesOffices.trigger( 2000000, 88 ); // 输出：200 万，88 平方米
	salesOffices.trigger( 3000000, 110 ); // 输出：300 万，110 平方米










	var salesOffices = {}; // 定义售楼处
	salesOffices.clientList = []; // 缓存列表，存放订阅者的回调函数

	salesOffices.listen = function( key, fn ){
		if ( !this.clientList[ key ] ){ // 如果还没有订阅过此类消息，给该类消息创建一个缓存列表
			this.clientList[ key ] = [];
		}
		this.clientList[ key ].push( fn ); // 订阅的消息添加进消息缓存列表
	};

	salesOffices.trigger = function(){ // 发布消息
		var key = Array.prototype.shift.call( arguments ), // 取出消息类型
		fns = this.clientList[ key ]; // 取出该消息对应的回调函数集合
		if ( !fns || fns.length === 0 ){ // 如果没有订阅该消息，则返回
			return false;
		}
		for( var i = 0, fn; fn = fns[ i++ ]; ){
			fn.apply( this, arguments ); // (2) // arguments 是发布消息时附送的参数
		}
	};

	salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅88 平方米房子的消息
		console.log( '价格= ' + price ); // 输出： 2000000
	});

	salesOffices.listen( 'squareMeter110', function( price ){ // 小红订阅110 平方米房子的消息
		console.log( '价格= ' + price ); // 输出： 3000000
	});

	salesOffices.trigger( 'squareMeter88', 2000000 ); // 发布88 平方米房子的价格
	salesOffices.trigger( 'squareMeter110', 3000000 ); // 发布110 平方米房子的价格



//所以我们把发布—订阅的功能提取出来，放在一个单独的对象内：
var event = {
	clientList: [],
	listen: function( key, fn ){
		if ( !this.clientList[ key ] ){
			this.clientList[ key ] = [];
		}
			this.clientList[ key ].push( fn ); // 订阅的消息添加进缓存列表
		},
		trigger: function(){
			var key = Array.prototype.shift.call( arguments ), // (1);
			fns = this.clientList[ key ];
			if ( !fns || fns.length === 0 ){ // 如果没有绑定对应的消息
				return false;
			}
			for( var i = 0, fn; fn = fns[ i++ ]; ){
				fn.apply( this, arguments ); // (2) // arguments 是trigger 时带上的参数
			}
		}
	};

	var installEvent = function( obj ){
		for ( var i in event ){
			obj[ i ] = event[ i ];
		}
	};
	//再来测试一番，我们给售楼处对象salesOffices 动态增加发布—订阅功能：
	var salesOffices = {};
	installEvent( salesOffices );
	salesOffices.listen( 'squareMeter88', function( price ){ // 小明订阅消息
		console.log( '价格= ' + price );
	});
	salesOffices.listen( 'squareMeter100', function( price ){ // 小红订阅消息
		console.log( '价格= ' + price );
	});
	salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出：2000000
	salesOffices.trigger( 'squareMeter100', 3000000 ); // 输出：3000000
</script>



<script type="text/javascript">
	event.remove = function( key, fn ){
		var fns = this.clientList[ key ];
		if ( !fns ){ // 如果key 对应的消息没有被人订阅，则直接返回
			return false;
		}
		if ( !fn ){ // 如果没有传入具体的回调函数，表示需要取消key 对应消息的所有订阅
			fns && ( fns.length = 0 );
		}else{
			for ( var l = fns.length - 1; l >=0; l-- ){ // 反向遍历订阅的回调函数列表
				var _fn = fns[ l ];
				if ( _fn === fn ){
					fns.splice( l, 1 ); // 删除订阅者的回调函数
				}
			}
		}
	};

	var salesOffices = {};
	var installEvent = function( obj ){
		for ( var i in event ){
			obj[ i ] = event[ i ];
		}
	}

	installEvent( salesOffices );

	salesOffices.listen( 'squareMeter88', fn1 = function( price ){ // 小明订阅消息
		console.log( '价格= ' + price );
	});

	salesOffices.listen( 'squareMeter88', fn2 = function( price ){ // 小红订阅消息
		console.log( '价格= ' + price );
	});

	salesOffices.remove( 'squareMeter88', fn1 ); // 删除小明的订阅
	salesOffices.trigger( 'squareMeter88', 2000000 ); // 输出：2000000
</script>


<script type="text/javascript">
	login.succ(function(data){
		header.setAvatar( data.avatar); // 设置header 模块的头像
		nav.setAvatar( data.avatar ); // 设置导航模块的头像
		message.refresh(); // 刷新消息列表
		cart.refresh(); // 刷新购物车列表
	});

	var header = (function(){ // header 模块
		login.listen( 'loginSucc', function( data){
			header.setAvatar( data.avatar );
		});
		return {
			setAvatar: function( data ){
				console.log( '设置header 模块的头像' );
			}
		}
	})();

	var nav = (function(){ // nav 模块
		login.listen( 'loginSucc', function( data ){
			nav.setAvatar( data.avatar );
		});
		return {
			setAvatar: function( avatar ){
				console.log( '设置nav 模块的头像' );
			}
		}
	})();

	var address = (function(){ // nav 模块
		login.listen( 'loginSucc', function( obj ){
			address.refresh( obj );
		});
		return {
			refresh: function( avatar ){
				console.log( '刷新收货地址列表' );
			}
		}
	})();

</script>


<script type="text/javascript">
	var Event = (function(){
		var clientList = {},
		listen,
		trigger,
		remove;
		listen = function( key, fn ){
			if ( !clientList[ key ] ){
				clientList[ key ] = [];
			}
			clientList[ key ].push( fn );
		};
		trigger = function(){
			var key = Array.prototype.shift.call( arguments ),
			fns = clientList[ key ];
			if ( !fns || fns.length === 0 ){
				return false;
			}
			for( var i = 0, fn; fn = fns[ i++ ]; ){
				fn.apply( this, arguments );
			}
		};
		remove = function( key, fn ){
			var fns = clientList[ key ];
			if ( !fns ){
				return false;
			}
			if ( !fn ){
				fns && ( fns.length = 0 );
			}else{
				for ( var l = fns.length - 1; l >=0; l-- ){
					var _fn = fns[ l ];
					if ( _fn === fn ){
						fns.splice( l, 1 );
					}
				}
			}
		};
		return {
			listen: listen,
			trigger: trigger,
			remove: remove
		}
	})();

	Event.listen( 'squareMeter88', function( price ){ // 小红订阅消息
		console.log( '价格= ' + price ); // 输出：'价格=2000000'
	});

	Event.trigger( 'squareMeter88', 2000000 ); // 售楼处发布消息
</script>


<html>
<body>
	<button id="count">点我</button>
	<div id="show"></div>
</body>
<script type="text/JavaScript">
	var a = (function(){
		var count = 0;
		var button = document.getElementById( 'count' );
		120 第8 章 发布订阅模式
		button.onclick = function(){
			Event.trigger( 'add', count++ );
		}
	})();
	var b = (function(){
		var div = document.getElementById( 'show' );
		Event.listen( 'add', function( count ){
			div.innerHTML = count;
		});
	})();
</script>
</html>

<script type="text/javascript">
	var Event = (function(){
		var global = this,
		Event,
		_default = 'default';
		Event = function(){
			var _listen,
			_trigger,
			_remove,
			_slice = Array.prototype.slice,
			_shift = Array.prototype.shift,
			_unshift = Array.prototype.unshift,
			namespaceCache = {},
			_create,
			find,
			each = function( ary, fn ){
				var ret;
				for ( var i = 0, l = ary.length; i < l; i++ ){
					var n = ary[i];
					ret = fn.call( n, i, n);
				}
				return ret;
			};
			_listen = function( key, fn, cache ){
				if ( !cache[ key ] ){
					cache[ key ] = [];
				}
				cache[key].push( fn );
			};
			_remove = function( key, cache ,fn){
				if ( cache[ key ] ){
					if( fn ){
						for( var i = cache[ key ].length; i >= 0; i-- ){
							if( cache[ key ] === fn ){
								cache[ key ].splice( i, 1 );
							}
						}
					}else{
						cache[ key ] = [];
					}
				}
			};
			_trigger = function(){
				var cache = _shift.call(arguments),
				key = _shift.call(arguments),
				args = arguments,
				_self = this,
				ret,
				stack = cache[ key ];
				if ( !stack || !stack.length ){
					return;
				}
				return each( stack, function(){
					return this.apply( _self, args );
				});
			};
			_create = function( namespace ){
				var namespace = namespace || _default;
				var cache = {},
				offlineStack = [], // 离线事件
				ret = {
					listen: function( key, fn, last ){
						_listen( key, fn, cache );
						if ( offlineStack === null ){
							return;
						}
						if ( last === 'last' ){
						}else{
							each( offlineStack, function(){
								this();
							});
						}
						offlineStack = null;
					},
					one: function( key, fn, last ){
						_remove( key, cache );
						this.listen( key, fn ,last );
					},
					remove: function( key, fn ){
						_remove( key, cache ,fn);
					},
					trigger: function(){
						var fn,
						args,
						_self = this;
						_unshift.call( arguments, cache );
						args = arguments;
						fn = function(){
							return _trigger.apply( _self, args );
						};
						if ( offlineStack ){
							return offlineStack.push( fn );
						}
						return fn();
					}
				};
				return namespace ?
				( namespaceCache[ namespace ] ? namespaceCache[ namespace ] :
					namespaceCache[ namespace ] = ret )
				: ret;
			};
			return {
				create: _create,
				one: function( key,fn, last ){
					var event = this.create( );
					event.one( key,fn,last );
				},
				remove: function( key,fn ){
					var event = this.create( );
					event.remove( key,fn );
				},
				listen: function( key, fn, last ){
					var event = this.create( );
					event.listen( key, fn, last );
				},
				trigger: function(){
					var event = this.create( );
					event.trigger.apply( this, arguments );
				}
			};
		}();
		return Event;
	})();

</script>